# -*- coding: utf-8 -*-
import json
from datetime import datetime

from sqlalchemy import or_

from common.bankcard.model import *
from common.utils import exceptions as err
from common.utils import track_logging
from common.utils.db import list_object, upsert
from common.utils.decorator import sql_wrapper

_LOGGER = track_logging.getLogger(__name__)


@sql_wrapper
def create_bankcard(data):
    try:
        bankcard = Bankcard()
        for k, v in data.iteritems():
            setattr(bankcard, k, v)
        bankcard.status = BANK_CARD_STATUS.UNAVAILABLE
        bankcard.card_type = BANK_CARD_TYPE.TEST
        bankcard.reached_limit = 0
        bankcard.deleted = 0
        bankcard.reached_daily_limit = 0
        bankcard.save(auto_commit=False)
        orm.session.commit()

        card_data = BankcardData()
        card_data.card_id = bankcard.id
        card_data.name = bankcard.name
        for item in ['recharge_count', 'withdraw_count', 'daily_recharge_count',
                     'daily_withdraw_count', 'recharge_total', 'withdraw_total', 'daily_recharge_total',
                     'daily_withdraw_total']:
            setattr(card_data, item, 0)
        card_data.created_at = datetime.utcnow()
        card_data.updated_at = datetime.utcnow()
        card_data.save(auto_commit=False)
        orm.session.commit()

        return bankcard
    except Exception as e:
        orm.session.rollback()
        _LOGGER.exception('create bank card error, %s', e)
        raise err.DataConflict()
    finally:
        orm.session.close()


@sql_wrapper
def batch_create_bankcard(data):
    try:
        for card in data:
            bankcard = Bankcard()
            for k, v in card.iteritems():
                setattr(bankcard, k, v)
            bankcard.status = BANK_CARD_STATUS.UNAVAILABLE
            bankcard.card_type = BANK_CARD_TYPE.TEST
            bankcard.reached_limit = 0
            bankcard.reached_daily_limit = 0
            bankcard.deleted = 0
            bankcard.save(auto_commit=False)

            card_data = BankcardData()
            card_data.card_id = bankcard.id
            card_data.name = bankcard.name
            for item in ['recharge_count', 'withdraw_count', 'daily_recharge_count',
                         'daily_withdraw_count', 'recharge_total', 'withdraw_total', 'daily_recharge_total',
                         'daily_withdraw_total']:
                setattr(card_data, item, 0)
            card_data.created_at = datetime.utcnow()
            card_data.updated_at = datetime.utcnow()
            card_data.save(auto_commit=False)
        orm.session.commit()
        orm.session.commit()
    except Exception as e:
        orm.session.rollback()
        _LOGGER.exception('create bank card error, %s', e)
        raise err.DataConflict("card conflict")
    finally:
        orm.session.close()
    return {}


@sql_wrapper
def update_bankcard_info(bankcard_id, data):
    return upsert(Bankcard, data, bankcard_id)


@sql_wrapper
def update_bankcard_peroid(bankcard_id, data):
    return upsert(BankcardPeroid, data, bankcard_id)


@sql_wrapper
def get_bankcard(bankcard_id):
    return Bankcard.query.filter(Bankcard.id == bankcard_id).first()


@sql_wrapper
def check_bankcard_exists(card_name, account_number):
    bankcard = Bankcard.query.filter(
        Bankcard.name == card_name).first()
    if not bankcard:
        bankcard = Bankcard.query.filter(
            Bankcard.account_number == account_number).first()
    return bankcard


@sql_wrapper
def get_bankcard_by_name(bankcard_name):
    return Bankcard.query.filter(Bankcard.name == bankcard_name).first()


@sql_wrapper
def get_bankcard_by_num(card_number):
    return Bankcard.query.filter(Bankcard.account_number == card_number).first()


@sql_wrapper
def update_real_balance(card_num, balance):
    card = Bankcard.query.filter(Bankcard.account_number == card_num).first()
    if card:
        card.real_balance = float(balance)
        card.save()


@sql_wrapper
def get_bankcard_by_context(mch_id, context, pay_type='bankcard', pay_account_num='', sdk_version=''):
    # TODO choose bank card via context data
    context = json.loads(context)

    # 检查context字段
    for param in ['account_day', 'total_recharge', 'total_bet',
                  'count_recharge', 'count_withdraw', 'user_id', 'device_ip']:
        if param not in context['user_info']:
            raise err.ParamError('context data error: missing %s' % param)
    # ratio若没传，计算一个
    if 'ratio' not in context['user_info']:
        if context['user_info'].get('total_recharge'):
            context['user_info']['ratio'] = \
                context['user_info']['total_bet'] / context['user_info']['total_recharge']
        else:
            context['user_info']['ratio'] = 0

    # 针对派卡
    bank_name = context.get('official_bank_no', None)

    context_key = ['account_day', 'total_recharge', 'total_bet', 'count_recharge', 'count_withdraw', 'ratio']
    context_list = [context['user_info'][key] for key in context_key]
    star = 0
    for star in range(1, MAXIMUM_STAR + 1):
        if not all(value >= STAR_STRATEGY[star][idx] for idx, value in enumerate(context_list)):
            star -= 1
            break
    _LOGGER.info('context with star: %s' % star)
    if bank_name:
        bankcard = get_bankcard_by_name(bank_name)
    elif sdk_version == 'Agent_recharge':
        bankcard = get_agent_bankcard(star, mch_id)
    else:
        bankcard = get_bankcard_by_mch_star(star, mch_id)
    return star, bankcard


@sql_wrapper
def get_withdraw_bankcard_by_name(bankcard_name, mch_id):
    return Bankcard.query.filter(
        Bankcard.name == bankcard_name).filter(
        Bankcard.mch_id == mch_id).filter(
        Bankcard.card_type == BANK_CARD_TYPE.WITHDRAW).filter(
        Bankcard.status == BANK_CARD_STATUS.AVAILABLE).first()


@sql_wrapper
def get_chargeable_bankcards_by_mch(mch_id):
    return Bankcard.query.filter(Bankcard.mch_id == mch_id).filter(
        Bankcard.card_type == BANK_CARD_TYPE.RECHARGE).filter(
        Bankcard.status != BANK_CARD_STATUS.UNAVAILABLE).all()


@sql_wrapper
def delete_bankcard(bankcard_id):
    card = Bankcard.query.filter(Bankcard.id == bankcard_id).first()
    card.delete()
    orm.session.commit()
    _LOGGER.info("delete_bankcard %s" % card.account_number)


@sql_wrapper
def list_bankcard(query_dct):
    card_ids = []
    star = query_dct.get('star')
    if star:
        cards = BankcardStar.query.filter(BankcardStar.star == int(star)).all()
        for card in cards:
            card_ids.append(card.card_id)
        query_dct['id'] = json.dumps({'$in': card_ids})
    query_dct['deleted'] = '0'
    return list_object(query_dct, Bankcard)


@sql_wrapper
def get_all_bankcards():
    bankcards = Bankcard.query.filter(Bankcard.deleted == 0).all()
    return bankcards


@sql_wrapper
def get_all_bankcard_peroid():
    bankcard_peroid = BankcardPeroid.query.all()
    return bankcard_peroid


@sql_wrapper
def create_peroid(data):
    try:
        bankcard_peroid = BankcardPeroid()
        for k, v in data.iteritems():
            setattr(bankcard_peroid, k, v)
        bankcard_peroid.save(auto_commit=False)
        orm.session.commit()
        return bankcard_peroid
    except Exception as e:
        orm.session.rollback()
        _LOGGER.exception('create bank card error, %s', e)
        raise err.DataError()
    finally:
        orm.session.close()


@sql_wrapper
def list_bankcard_peroid(query_dct):
    return list_object(query_dct, BankcardPeroid)


@sql_wrapper
def get_bankcard_peroid(peroid_id):
    return BankcardPeroid.query.filter(BankcardPeroid.id == peroid_id).first()


@sql_wrapper
def check_peroid_exists(card_type, bank):
    return BankcardPeroid.query.filter(
        BankcardPeroid.card_type == card_type).filter(
        BankcardPeroid.bank == bank).first()


@sql_wrapper
def delete_bankcard_peroid(peroid_id):
    BankcardPeroid.query.filter(BankcardPeroid.id == peroid_id).delete()
    orm.session.commit()
    return {}


@sql_wrapper
def update_bankcard_stars(card_id, stars):
    BankcardStar.query.filter(
        BankcardStar.card_id == card_id).delete()
    for star in stars:
        card_star = BankcardStar()
        card_star.card_id = card_id
        card_star.star = star
        card_star.save(auto_commit=False)
    orm.session.commit()
    return {}


@sql_wrapper
def get_bankcard_by_mch_star(star, mch_id):
    bankcard = orm.session.query(Bankcard).filter(
        Bankcard.status == BANK_CARD_STATUS.AVAILABLE).filter(
        Bankcard.reached_limit == 0).filter(
        Bankcard.reached_daily_limit == 0).filter(
        Bankcard.card_type == BANK_CARD_TYPE.RECHARGE).filter(
        Bankcard.mch_id == mch_id).join(
        BankcardStar, Bankcard.id == BankcardStar.card_id).filter(
        BankcardStar.star == star).order_by(Bankcard.real_balance).first()
    return bankcard


@sql_wrapper
def get_no_hydra_bankcard(star, mch_id):
    bankcard = orm.session.query(Bankcard).filter(
        Bankcard.status == BANK_CARD_STATUS.AVAILABLE).filter(
        Bankcard.reached_limit == 0).filter(
        Bankcard.card_type != BANK_CARD_TYPE.ORECHARGE).filter(
        Bankcard.reached_daily_limit == 0).filter(
        Bankcard.mch_id == mch_id).join(
        BankcardStar, Bankcard.id == BankcardStar.card_id).filter(
        BankcardStar.star == star).order_by(Bankcard.real_balance).first()
    return bankcard


@sql_wrapper
def get_bankcard_stars(card_id):
    stars = []
    card_stars = BankcardStar.query.filter(BankcardStar.card_id == card_id).all()
    for card_star in card_stars:
        stars.append(card_star.star)
    return stars


@sql_wrapper
def get_all_bankcard_stars():
    resp = {}
    card_stars = BankcardStar.query.order_by(BankcardStar.star).all()
    for card_star in card_stars:
        if card_star.card_id not in resp:
            resp[card_star.card_id] = []
        resp[card_star.card_id].append(card_star.star)
    return resp


@sql_wrapper
def get_all_bankcard_data():
    resp = {}
    card_datas = BankcardData.query.all()
    for card_data in card_datas:
        resp[card_data.card_id] = card_data.as_dict()
    return resp


@sql_wrapper
def get_bankcard_data(card_id):
    bankcard = Bankcard.query.filter(Bankcard.id == card_id).first()
    card_data = BankcardData.query.filter(
        BankcardData.card_id == card_id).first()
    if bankcard and not card_data:
        card_data = create_bankcard_data(bankcard.id, bankcard.name)
    return card_data


@sql_wrapper
def create_bankcard_data(card_id, name):
    card_data = BankcardData()
    card_data.card_id = card_id
    card_data.name = name
    for item in ['recharge_count', 'withdraw_count', 'daily_recharge_count',
                 'daily_withdraw_count', 'recharge_total', 'withdraw_total', 'daily_recharge_total',
                 'daily_withdraw_total']:
        setattr(card_data, item, 0)
    card_data.created_at = datetime.utcnow()
    card_data.updated_at = datetime.utcnow()
    card_data.save()
    return card_data


@sql_wrapper
def get_agent_bankcard(star, mch_id):
    bankcard = orm.session.query(Bankcard).filter(
        Bankcard.status == BANK_CARD_STATUS.AVAILABLE).filter(
        Bankcard.reached_limit == 0).filter(
        Bankcard.card_type == BANK_CARD_TYPE.AGENT_RECHARGE).filter(
        Bankcard.reached_daily_limit == 0).filter(
        Bankcard.mch_id == mch_id).join(
        BankcardStar, Bankcard.id == BankcardStar.card_id).filter(
        BankcardStar.star == star).order_by(Bankcard.real_balance).first()
    return bankcard


@sql_wrapper
def get_bankcard_by_type_status(card_type, status, sent_time):
    cards = []
    card_data = Bankcard.query.filter(or_(Bankcard.reminder_sent_at == None, Bankcard.reminder_sent_at <= sent_time)) \
        .filter(Bankcard.deleted == 0).filter(Bankcard.card_type == card_type) \
        .filter(Bankcard.status == status).all()

    if card_data:
        for card in card_data:
            cards.append(card)
    return cards
